/*!
 * jCarousel - Riding carousels with jQuery
 *   http://sorgalla.com/jcarousel/
 *
 * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 *
 * Built on top of the jQuery library
 *   http://jquery.com
 *
 * Inspired by the "Carousel Component" by Bill Scott
 *   http://billwscott.com/carousel/
 */

(function($) {
	/**
	 * Creates a carousel for all matched elements.
	 * 
	 * @example $("#mycarousel").jcarousel();
	 * @before
	 *          <ul id="mycarousel" class="jcarousel-skin-name">
	 *          <li>First item</li>
	 *          <li>Second item</li>
	 *          </ul>
	 * @result
	 * 
	 * <div class="jcarousel-skin-name"> <div class="jcarousel-container"> <div
	 * class="jcarousel-clip">
	 * <ul class="jcarousel-list">
	 * <li class="jcarousel-item-1">First item</li>
	 * <li class="jcarousel-item-2">Second item</li>
	 * </ul>
	 * </div> <div disabled="disabled" class="jcarousel-prev
	 * jcarousel-prev-disabled"></div> <div class="jcarousel-next"></div>
	 * </div> </div>
	 * 
	 * @method jcarousel
	 * @return jQuery
	 * @param o
	 *            {Hash|String} A set of key/value pairs to set as configuration
	 *            properties or a method name to call on a formerly created
	 *            instance.
	 */
	$.fn.jcarousel = function(o) {
		if (typeof o == 'string') {
			var instance = $(this).data('jcarousel'), args = Array.prototype.slice
					.call(arguments, 1);
			return instance[o].apply(instance, args);
		} else
			return this.each(function() {
				$(this).data('jcarousel', new $jc(this, o));
			});
	};

	// Default configuration properties.
	var defaults = {
		vertical : false,
		start : 1,
		offset : 1,
		size : null,
		scroll : 9,
		visible : null,
		animation : 'normal',
		easing : 'swing',
		auto : 0,
		wrap : null,
		initCallback : null,
		reloadCallback : null,
		itemLoadCallback : null,
		itemFirstInCallback : null,
		itemFirstOutCallback : null,
		itemLastInCallback : null,
		itemLastOutCallback : null,
		itemVisibleInCallback : null,
		itemVisibleOutCallback : null,
		buttonNextHTML : '<div></div>',
		buttonPrevHTML : '<div></div>',
		buttonNextEvent : 'click',
		buttonPrevEvent : 'click',
		buttonNextCallback : null,
		buttonPrevCallback : null
	};

	/**
	 * The jCarousel object.
	 * 
	 * @constructor
	 * @class jcarousel
	 * @param e
	 *            {HTMLElement} The element to create the carousel for.
	 * @param o
	 *            {Object} A set of key/value pairs to set as configuration
	 *            properties.
	 * @cat Plugins/jCarousel
	 */
	$.jcarousel = function(e, o) {
		this.options = $.extend({}, defaults, o || {});

		this.locked = false;

		this.container = null;
		this.clip = null;
		this.list = null;
		this.buttonNext = null;
		this.buttonPrev = null;

		this.wh = !this.options.vertical ? 'width' : 'height';
		this.lt = !this.options.vertical ? 'left' : 'top';

		// Extract skin class
		var skin = '', split = e.className.split(' ');

		for ( var i = 0; i < split.length; i++) {
			if (split[i].indexOf('jcarousel-skin') != -1) {
				$(e).removeClass(split[i]);
				skin = split[i];
				break;
			}
		}

		if (e.nodeName == 'UL' || e.nodeName == 'OL') {
			this.list = $(e);
			this.container = this.list.parent();

			if (this.container.hasClass('jcarousel-clip')) {
				if (!this.container.parent().hasClass('jcarousel-container'))
					this.container = this.container.wrap('<div></div>');

				this.container = this.container.parent();
			} else if (!this.container.hasClass('jcarousel-container'))
				this.container = this.list.wrap('<div></div>').parent();
		} else {
			this.container = $(e);
			this.list = this.container.find('ul,ol').eq(0);
		}

		if (skin != ''
				&& this.container.parent()[0].className
						.indexOf('jcarousel-skin') == -1)
			this.container.wrap('<div class=" ' + skin + '"></div>');

		this.clip = this.list.parent();

		if (!this.clip.length || !this.clip.hasClass('jcarousel-clip'))
			this.clip = this.list.wrap('<div></div>').parent();

		this.buttonNext = $('.jcarousel-next', this.container);

		if (this.buttonNext.size() == 0 && this.options.buttonNextHTML != null)
			this.buttonNext = this.clip.after(this.options.buttonNextHTML)
					.next();

		this.buttonNext.addClass(this.className('jcarousel-next'));

		this.buttonPrev = $('.jcarousel-prev', this.container);

		if (this.buttonPrev.size() == 0 && this.options.buttonPrevHTML != null)
			this.buttonPrev = this.clip.after(this.options.buttonPrevHTML)
					.next();

		this.buttonPrev.addClass(this.className('jcarousel-prev'));

		this.clip.addClass(this.className('jcarousel-clip')).css({
			overflow : 'hidden',
			position : 'relative'
		});
		this.list.addClass(this.className('jcarousel-list')).css({
			overflow : 'hidden',
			position : 'relative',
			top : 0,
			left : 0,
			margin : 0,
			padding : 0
		});
		this.container.addClass(this.className('jcarousel-container')).css({
			position : 'relative'
		});

		var di = this.options.visible != null ? Math.ceil(this.clipping()
				/ this.options.visible) : null;
		var li = this.list.children('li');

		var self = this;

		if (li.size() > 0) {
			var wh = 0, i = this.options.offset;
			li.each(function() {
				self.format(this, i++);
				wh += self.dimension(this, di);
			});

			this.list.css(this.wh, wh + 'px');

			// Only set if not explicitly passed as option
			if (!o || o.size === undefined)
				this.options.size = li.size();
		}

		// For whatever reason, .show() does not work in Safari...
		this.container.css('display', 'block');
		this.buttonNext.css('display', 'block');
		this.buttonPrev.css('display', 'block');

		this.funcNext = function() {
			self.next();
		};
		this.funcPrev = function() {
			self.prev();
		};
		this.funcResize = function() {
			self.reload();
		};

		if (this.options.initCallback != null)
			this.options.initCallback(this, 'init');

		if ($.browser.safari) {
			this.buttons(false, false);
			$(window).bind('load.jcarousel', function() {
				self.setup();
			});
		} else
			this.setup();
	};

	// Create shortcut for internal use
	var $jc = $.jcarousel;

	$jc.fn = $jc.prototype = {
		jcarousel : '0.2.4'
	};

	$jc.fn.extend = $jc.extend = $.extend;

	$jc.fn
			.extend({
				/**
				 * Setups the carousel.
				 * 
				 * @method setup
				 * @return undefined
				 */
				setup : function() {
					this.first = null;
					this.last = null;
					this.prevFirst = null;
					this.prevLast = null;
					this.animating = false;
					this.timer = null;
					this.tail = null;
					this.inTail = false;

					if (this.locked)
						return;

					this.list
							.css(this.lt, this.pos(this.options.offset) + 'px');
					var p = this.pos(this.options.start);
					this.prevFirst = this.prevLast = null;
					this.animate(p, false);

					$(window).unbind('resize.jcarousel', this.funcResize).bind(
							'resize.jcarousel', this.funcResize);
				},

				/**
				 * Clears the list and resets the carousel.
				 * 
				 * @method reset
				 * @return undefined
				 */
				reset : function() {
					this.list.empty();

					this.list.css(this.lt, '0px');
					this.list.css(this.wh, '10px');

					if (this.options.initCallback != null)
						this.options.initCallback(this, 'reset');

					this.setup();
				},

				/**
				 * Reloads the carousel and adjusts positions.
				 * 
				 * @method reload
				 * @return undefined
				 */
				reload : function() {
					if (this.tail != null && this.inTail)
						this.list.css(this.lt, $jc.intval(this.list
								.css(this.lt))
								+ this.tail);

					this.tail = null;
					this.inTail = false;

					if (this.options.reloadCallback != null)
						this.options.reloadCallback(this);

					if (this.options.visible != null) {
						var self = this;
						var di = Math.ceil(this.clipping()
								/ this.options.visible), wh = 0, lt = 0;
						$('li', this.list).each(function(i) {
							wh += self.dimension(this, di);
							if (i + 1 < self.first)
								lt = wh;
						});

						this.list.css(this.wh, wh + 'px');
						this.list.css(this.lt, -lt + 'px');
					}

					this.scroll(this.first, false);
				},

				/**
				 * Locks the carousel.
				 * 
				 * @method lock
				 * @return undefined
				 */
				lock : function() {
					this.locked = true;
					this.buttons();
				},

				/**
				 * Unlocks the carousel.
				 * 
				 * @method unlock
				 * @return undefined
				 */
				unlock : function() {
					this.locked = false;
					this.buttons();
				},

				/**
				 * Sets the size of the carousel.
				 * 
				 * @method size
				 * @return undefined
				 * @param s
				 *            {Number} The size of the carousel.
				 */
				size : function(s) {
					if (s != undefined) {
						this.options.size = s;
						if (!this.locked)
							this.buttons();
					}

					return this.options.size;
				},

				/**
				 * Checks whether a list element exists for the given index (or
				 * index range).
				 * 
				 * @method get
				 * @return bool
				 * @param i
				 *            {Number} The index of the (first) element.
				 * @param i2
				 *            {Number} The index of the last element.
				 */
				has : function(i, i2) {
					if (i2 == undefined || !i2)
						i2 = i;

					if (this.options.size !== null && i2 > this.options.size)
						i2 = this.options.size;

					for ( var j = i; j <= i2; j++) {
						var e = this.get(j);
						if (!e.length
								|| e.hasClass('jcarousel-item-placeholder'))
							return false;
					}

					return true;
				},

				/**
				 * Returns a jQuery object with list element for the given
				 * index.
				 * 
				 * @method get
				 * @return jQuery
				 * @param i
				 *            {Number} The index of the element.
				 */
				get : function(i) {
					return $('.jcarousel-item-' + i, this.list);
				},

				/**
				 * Adds an element for the given index to the list. If the
				 * element already exists, it updates the inner html. Returns
				 * the created element as jQuery object.
				 * 
				 * @method add
				 * @return jQuery
				 * @param i
				 *            {Number} The index of the element.
				 * @param s
				 *            {String} The innerHTML of the element.
				 */
				add : function(i, s) {
					var e = this.get(i), old = 0, add = 0;

					if (e.length == 0) {
						var c, e = this.create(i), j = $jc.intval(i);
						while (c = this.get(--j)) {
							if (j <= 0 || c.length) {
								j <= 0 ? this.list.prepend(e) : c.after(e);
								break;
							}
						}
					} else
						old = this.dimension(e);

					e.removeClass(this.className('jcarousel-item-placeholder'));
					typeof s == 'string' ? e.html(s) : e.empty().append(s);

					var di = this.options.visible != null ? Math.ceil(this
							.clipping()
							/ this.options.visible) : null;
					var wh = this.dimension(e, di) - old;

					if (i > 0 && i < this.first)
						this.list.css(this.lt, $jc.intval(this.list
								.css(this.lt))
								- wh + 'px');

					this.list.css(this.wh, $jc.intval(this.list.css(this.wh))
							+ wh + 'px');

					return e;
				},

				/**
				 * Removes an element for the given index from the list.
				 * 
				 * @method remove
				 * @return undefined
				 * @param i
				 *            {Number} The index of the element.
				 */
				remove : function(i) {
					var e = this.get(i);

					// Check if item exists and is not currently visible
					if (!e.length || (i >= this.first && i <= this.last))
						return;

					var d = this.dimension(e);

					if (i < this.first)
						this.list.css(this.lt, $jc.intval(this.list
								.css(this.lt))
								+ d + 'px');

					e.remove();

					this.list.css(this.wh, $jc.intval(this.list.css(this.wh))
							- d + 'px');
				},

				/**
				 * Moves the carousel forwards.
				 * 
				 * @method next
				 * @return undefined
				 */
				next : function() {
					this.stopAuto();

					if (this.tail != null && !this.inTail)
						this.scrollTail(false);
					else
						this
								.scroll(((this.options.wrap == 'both' || this.options.wrap == 'last')
										&& this.options.size != null && this.last == this.options.size) ? 1
										: this.first + this.options.scroll);
				},

				/**
				 * Moves the carousel backwards.
				 * 
				 * @method prev
				 * @return undefined
				 */
				prev : function() {
					this.stopAuto();

					if (this.tail != null && this.inTail)
						this.scrollTail(true);
					else
						this
								.scroll(((this.options.wrap == 'both' || this.options.wrap == 'first')
										&& this.options.size != null && this.first == 1) ? this.options.size
										: this.first - this.options.scroll);
				},

				/**
				 * Scrolls the tail of the carousel.
				 * 
				 * @method scrollTail
				 * @return undefined
				 * @param b
				 *            {Boolean} Whether scroll the tail back or forward.
				 */
				scrollTail : function(b) {
					if (this.locked || this.animating || !this.tail)
						return;

					var pos = $jc.intval(this.list.css(this.lt));

					!b ? pos -= this.tail : pos += this.tail;
					this.inTail = !b;

					// Save for callbacks
					this.prevFirst = this.first;
					this.prevLast = this.last;

					this.animate(pos);
				},

				/**
				 * Scrolls the carousel to a certain position.
				 * 
				 * @method scroll
				 * @return undefined
				 * @param i
				 *            {Number} The index of the element to scoll to.
				 * @param a
				 *            {Boolean} Flag indicating whether to perform
				 *            animation.
				 */
				scroll : function(i, a) {
					if (this.locked || this.animating)
						return;

					this.animate(this.pos(i), a);
				},

				/**
				 * Prepares the carousel and return the position for a certian
				 * index.
				 * 
				 * @method pos
				 * @return {Number}
				 * @param i
				 *            {Number} The index of the element to scoll to.
				 */
				pos : function(i) {
					var pos = $jc.intval(this.list.css(this.lt));

					if (this.locked || this.animating)
						return pos;

					if (this.options.wrap != 'circular')
						i = i < 1 ? 1 : (this.options.size
								&& i > this.options.size ? this.options.size
								: i);

					var back = this.first > i;

					// Create placeholders, new list width/height
					// and new list position
					var f = this.options.wrap != 'circular' && this.first <= 1 ? 1
							: this.first;
					var c = back ? this.get(f) : this.get(this.last);
					var j = back ? f : f - 1;
					var e = null, l = 0, p = false, d = 0, g;

					while (back ? --j >= i : ++j < i) {
						e = this.get(j);
						p = !e.length;
						if (e.length == 0) {
							e = this
									.create(j)
									.addClass(
											this
													.className('jcarousel-item-placeholder'));
							c[back ? 'before' : 'after'](e);

							if (this.first != null
									&& this.options.wrap == 'circular'
									&& this.options.size !== null
									&& (j <= 0 || j > this.options.size)) {
								g = this.get(this.index(j));
								if (g.length)
									this.add(j, g.children().clone(true));
							}
						}

						c = e;
						d = this.dimension(e);

						if (p)
							l += d;

						if (this.first != null
								&& (this.options.wrap == 'circular' || (j >= 1 && (this.options.size == null || j <= this.options.size))))
							pos = back ? pos + d : pos - d;
					}

					// Calculate visible items
					var clipping = this.clipping();
					var cache = [];
					var visible = 0, j = i, v = 0;
					var c = this.get(i - 1);

					while (++visible) {
						e = this.get(j);
						p = !e.length;
						if (e.length == 0) {
							e = this
									.create(j)
									.addClass(
											this
													.className('jcarousel-item-placeholder'));
							// This should only happen on a next scroll
							c.length == 0 ? this.list.prepend(e)
									: c[back ? 'before' : 'after'](e);

							if (this.first != null
									&& this.options.wrap == 'circular'
									&& this.options.size !== null
									&& (j <= 0 || j > this.options.size)) {
								g = this.get(this.index(j));
								if (g.length)
									this.add(j, g.find('>*').clone(true));
							}
						}

						c = e;
						var d = this.dimension(e);
						if (d == 0) {
							alert('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
							return 0;
						}

						if (this.options.wrap != 'circular'
								&& this.options.size !== null
								&& j > this.options.size)
							cache.push(e);
						else if (p)
							l += d;

						v += d;

						if (v >= clipping)
							break;

						j++;
					}

					// Remove out-of-range placeholders
					for ( var x = 0; x < cache.length; x++)
						cache[x].remove();

					// Resize list
					if (l > 0) {
						this.list.css(this.wh, this.dimension(this.list) + l
								+ 'px');

						if (back) {
							pos -= l;
							this.list.css(this.lt, $jc.intval(this.list
									.css(this.lt))
									- l + 'px');
						}
					}

					// Calculate first and last item
					var last = i + visible - 1;
					if (this.options.wrap != 'circular' && this.options.size
							&& last > this.options.size)
						last = this.options.size;

					if (j > last) {
						visible = 0, j = last, v = 0;
						while (++visible) {
							var e = this.get(j--);
							if (!e.length)
								break;
							v += this.dimension(e);
							if (v >= clipping)
								break;
						}
					}

					var first = last - visible + 1;
					if (this.options.wrap != 'circular' && first < 1)
						first = 1;

					if (this.inTail && back) {
						pos += this.tail;
						this.inTail = false;
					}

					this.tail = null;
					if (this.options.wrap != 'circular'
							&& last == this.options.size
							&& (last - visible + 1) >= 1) {
						var m = $jc.margin(this.get(last),
								!this.options.vertical ? 'marginRight'
										: 'marginBottom');
						if ((v - m) > clipping)
							this.tail = v - clipping - m;
					}

					// Adjust position
					while (i-- > first)
						pos += this.dimension(this.get(i));

					// Save visible item range
					this.prevFirst = this.first;
					this.prevLast = this.last;
					this.first = first;
					this.last = last;

					return pos;
				},

				/**
				 * Animates the carousel to a certain position.
				 * 
				 * @method animate
				 * @return undefined
				 * @param p
				 *            {Number} Position to scroll to.
				 * @param a
				 *            {Boolean} Flag indicating whether to perform
				 *            animation.
				 */
				animate : function(p, a) {
					if (this.locked || this.animating)
						return;

					this.animating = true;

					var self = this;
					var scrolled = function() {
						self.animating = false;

						if (p == 0)
							self.list.css(self.lt, 0);

						if (self.options.wrap == 'circular'
								|| self.options.wrap == 'both'
								|| self.options.wrap == 'last'
								|| self.options.size == null
								|| self.last < self.options.size)
							self.startAuto();

						self.buttons();
						self.notify('onAfterAnimation');
					};

					this.notify('onBeforeAnimation');

					// Animate
					if (!this.options.animation || a == false) {
						this.list.css(this.lt, p + 'px');
						scrolled();
					} else {
						var o = !this.options.vertical ? {
							'left' : p
						} : {
							'top' : p
						};
						this.list.animate(o, this.options.animation,
								this.options.easing, scrolled);
					}
				},

				/**
				 * Starts autoscrolling.
				 * 
				 * @method auto
				 * @return undefined
				 * @param s
				 *            {Number} Seconds to periodically autoscroll the
				 *            content.
				 */
				startAuto : function(s) {
					if (s != undefined)
						this.options.auto = s;

					if (this.options.auto == 0)
						return this.stopAuto();

					if (this.timer != null)
						return;

					var self = this;
					this.timer = setTimeout(function() {
						self.next();
					}, this.options.auto * 1000);
				},

				/**
				 * Stops autoscrolling.
				 * 
				 * @method stopAuto
				 * @return undefined
				 */
				stopAuto : function() {
					if (this.timer == null)
						return;

					clearTimeout(this.timer);
					this.timer = null;
				},

				/**
				 * Sets the states of the prev/next buttons.
				 * 
				 * @method buttons
				 * @return undefined
				 */
				buttons : function(n, p) {
					if (n == undefined || n == null) {
						var n = !this.locked
								&& this.options.size !== 0
								&& ((this.options.wrap && this.options.wrap != 'first')
										|| this.options.size == null || this.last < this.options.size);
						if (!this.locked
								&& (!this.options.wrap || this.options.wrap == 'first')
								&& this.options.size != null
								&& this.last >= this.options.size)
							n = this.tail != null && !this.inTail;
					}

					if (p == undefined || p == null) {
						var p = !this.locked
								&& this.options.size !== 0
								&& ((this.options.wrap && this.options.wrap != 'last') || this.first > 1);
						if (!this.locked
								&& (!this.options.wrap || this.options.wrap == 'last')
								&& this.options.size != null && this.first == 1)
							p = this.tail != null && this.inTail;
					}

					var self = this;

					this.buttonNext[n ? 'bind' : 'unbind'](
							this.options.buttonNextEvent + '.jcarousel',
							this.funcNext)[n ? 'removeClass' : 'addClass'](
							this.className('jcarousel-next-disabled')).attr(
							'disabled', n ? false : true);
					this.buttonPrev[p ? 'bind' : 'unbind'](
							this.options.buttonPrevEvent + '.jcarousel',
							this.funcPrev)[p ? 'removeClass' : 'addClass'](
							this.className('jcarousel-prev-disabled')).attr(
							'disabled', p ? false : true);

					if (this.buttonNext.length > 0
							&& (this.buttonNext[0].jcarouselstate == undefined || this.buttonNext[0].jcarouselstate != n)
							&& this.options.buttonNextCallback != null) {
						this.buttonNext.each(function() {
							self.options.buttonNextCallback(self, this, n);
						});
						this.buttonNext[0].jcarouselstate = n;
					}

					if (this.buttonPrev.length > 0
							&& (this.buttonPrev[0].jcarouselstate == undefined || this.buttonPrev[0].jcarouselstate != p)
							&& this.options.buttonPrevCallback != null) {
						this.buttonPrev.each(function() {
							self.options.buttonPrevCallback(self, this, p);
						});
						this.buttonPrev[0].jcarouselstate = p;
					}
				},

				/**
				 * Notify callback of a specified event.
				 * 
				 * @method notify
				 * @return undefined
				 * @param evt
				 *            {String} The event name
				 */
				notify : function(evt) {
					var state = this.prevFirst == null ? 'init'
							: (this.prevFirst < this.first ? 'next' : 'prev');

					// Load items
					this.callback('itemLoadCallback', evt, state);

					if (this.prevFirst !== this.first) {
						this.callback('itemFirstInCallback', evt, state,
								this.first);
						this.callback('itemFirstOutCallback', evt, state,
								this.prevFirst);
					}

					if (this.prevLast !== this.last) {
						this.callback('itemLastInCallback', evt, state,
								this.last);
						this.callback('itemLastOutCallback', evt, state,
								this.prevLast);
					}

					this.callback('itemVisibleInCallback', evt, state,
							this.first, this.last, this.prevFirst,
							this.prevLast);
					this.callback('itemVisibleOutCallback', evt, state,
							this.prevFirst, this.prevLast, this.first,
							this.last);
				},

				callback : function(cb, evt, state, i1, i2, i3, i4) {
					if (this.options[cb] == undefined
							|| (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation'))
						return;

					var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt]
							: this.options[cb];

					if (!$.isFunction(callback))
						return;

					var self = this;

					if (i1 === undefined)
						callback(self, state, evt);
					else if (i2 === undefined)
						this.get(i1).each(function() {
							callback(self, this, i1, state, evt);
						});
					else {
						for ( var i = i1; i <= i2; i++)
							if (i !== null && !(i >= i3 && i <= i4))
								this.get(i).each(function() {
									callback(self, this, i, state, evt);
								});
					}
				},

				create : function(i) {
					return this.format('<li></li>', i);
				},

				format : function(e, i) {
					var $e = $(e).addClass(this.className('jcarousel-item'))
							.addClass(this.className('jcarousel-item-' + i))
							.css({
								'float' : 'left',
								'list-style' : 'none'
							});
					$e.attr('jcarouselindex', i);
					return $e;
				},

				className : function(c) {
					return c
							+ ' '
							+ c
							+ (!this.options.vertical ? '-horizontal'
									: '-vertical');
				},

				dimension : function(e, d) {
					var el = e.jquery != undefined ? e[0] : e;

					var old = !this.options.vertical ? el.offsetWidth
							+ $jc.margin(el, 'marginLeft')
							+ $jc.margin(el, 'marginRight') : el.offsetHeight
							+ $jc.margin(el, 'marginTop')
							+ $jc.margin(el, 'marginBottom');

					if (d == undefined || old == d)
						return old;

					var w = !this.options.vertical ? d
							- $jc.margin(el, 'marginLeft')
							- $jc.margin(el, 'marginRight') : d
							- $jc.margin(el, 'marginTop')
							- $jc.margin(el, 'marginBottom');

					$(el).css(this.wh, w + 'px');

					return this.dimension(el);
				},

				clipping : function() {
					return !this.options.vertical ? this.clip[0].offsetWidth
							- $jc.intval(this.clip.css('borderLeftWidth'))
							- $jc.intval(this.clip.css('borderRightWidth'))
							: this.clip[0].offsetHeight
									- $jc.intval(this.clip
											.css('borderTopWidth'))
									- $jc.intval(this.clip
											.css('borderBottomWidth'));
				},

				index : function(i, s) {
					if (s == undefined)
						s = this.options.size;

					return Math.round((((i - 1) / s) - Math.floor((i - 1) / s))
							* s) + 1;
				}
			});

	$jc.extend({
		/**
		 * Gets/Sets the global default configuration properties.
		 * 
		 * @method defaults
		 * @return {Object}
		 * @param d
		 *            {Object} A set of key/value pairs to set as configuration
		 *            properties.
		 */
		defaults : function(d) {
			return $.extend(defaults, d || {});
		},

		margin : function(e, p) {
			if (!e)
				return 0;

			var el = e.jquery != undefined ? e[0] : e;

			if (p == 'marginRight' && $.browser.safari) {
				var old = {
					'display' : 'block',
					'float' : 'none',
					'width' : 'auto'
				}, oWidth, oWidth2;

				$.swap(el, old, function() {
					oWidth = el.offsetWidth;
				});

				old['marginRight'] = 0;
				$.swap(el, old, function() {
					oWidth2 = el.offsetWidth;
				});

				return oWidth2 - oWidth;
			}

			return $jc.intval($.css(el, p));
		},

		intval : function(v) {
			v = parseInt(v);
			return isNaN(v) ? 0 : v;
		}
	});

})(jQuery);
